type canvasOption = {
	x?: number;
	y?: number;
	width?: number;
	height?: number;
	lineWidth?: number;
	strokeStyle?: string;
	fillStyle?: string;
};
type Ctx = ReturnType<typeof uni.createCanvasContext>;

export const drawQuadrilateral = (ctx: Ctx, option: canvasOption) => {
	let x = option.x || 0; //起始点x坐标
	let y = option.y || 0; //起始点y坐标
	let width = option.width || 100; //宽
	let height = option.height || option.width || 100; //高
	let lineWidth = option.lineWidth || 1; //边框宽度
	let strokeStyle = option.strokeStyle || '#fff'; //边线的颜色
	let fillStyle = option.fillStyle || '#fff'; //填充的颜色
	//开始路径
	ctx.beginPath();
	ctx.moveTo(x, y);
	// 绘制top
	ctx.lineTo(x + width, y);
	// // 绘制right
	ctx.lineTo(x + width, y + height);
	// // 绘制bottom
	ctx.lineTo(x, y + height);

	ctx.closePath();
	//路径闭合
	ctx.strokeStyle = strokeStyle;
	ctx.lineWidth = lineWidth;
	// ctx.lineJoin = 'round';
	ctx.stroke();
	ctx.fillStyle = fillStyle;
	ctx.fill();
};
type QrCodeOption = {
	x: number;
	y: number;
	size: number;
	modules: Array<string[]>;
	margin: number;
};
export const drawQRCode = (ctx: Ctx, { x, y, size, modules, margin }: QrCodeOption) => {
	ctx.fillRect(x, y, size, size);
	let length = modules.length;
	let tileSize = (size - margin * 2) / length;
	for (let row = 0; row < length; row++) {
		for (let col = 0; col < length; col++) {
			// 计算每一个小块的位置
			let blockX = col * tileSize + margin + x;
			let blockY = row * tileSize + margin + y;
			let w = tileSize;
			let h = tileSize;

			let style = modules[row][col] ? '#000' : '#fff';
			ctx.setFillStyle(style);
			ctx.fillRect(blockX, blockY, w, h);
		}
	}
};
type RectangleOption = {
	x: number;
	y: number;
	width: number;
	height: number;
	radius: number;
	lineWidth?: number;
	strokeStyle?: string;
	fillStyle?: string;
};
const roundedRectangle = (ctx: Ctx, { x, y, width, height, radius }: RectangleOption) => {
	if (width <= 0 || height <= 0) {
		ctx.arc(x, y, radius, 0, Math.PI * 2);
		return;
	}

	ctx.moveTo(x + radius, y);
	ctx.arcTo(x + width, y, x + width, y + height, radius);
	ctx.arcTo(x + width, y + height, x, y + height, radius);
	ctx.arcTo(x, y + height, x, y, radius);
	ctx.arcTo(x, y, x + radius, y, radius);
};

// 绘制 圆角矩形
export const drawRoundedRectangle = (ctx: Ctx, { strokeStyle = '#fff', fillStyle = '#fff', lineWidth = 1, x, y, width, height, radius }: RectangleOption) => {
	ctx.beginPath();
	ctx.lineWidth = lineWidth;
	roundedRectangle(ctx, { x, y, width, height, radius });
	ctx.strokeStyle = strokeStyle;
	ctx.fillStyle = fillStyle;
	ctx.stroke();
	ctx.fill();
};
type BorderImageOption = {
	x: number;
	y: number;
	width: number;
	height: number;
	radius: number;
	fillStyle?: string;
	strokeStyle?: string;
	imgOption: {
		url: string;
		x: number;
		y: number;
		w: number;
		h?: number;
	};
};
// 绘制圆角的图片 因为使用了clip，所有之后的绘图都会被限制在被剪切的区域内（不能访问画布上的其他区域）
// 文档 https://uniapp.dcloud.net.cn/api/canvas/CanvasContext.html#canvascontext-clip
export const drawBorderImage = (ctx: Ctx, { x, y, width, height, radius, fillStyle = '#fff', strokeStyle = '#fff', imgOption }: BorderImageOption) => {
	ctx.save();
	ctx.beginPath();
	drawRoundedRectangle(ctx, {
		x,
		y,
		width,
		height,
		radius,
		fillStyle,
		strokeStyle
	});
	ctx.closePath();
	ctx.clip();
	ctx.drawImage(imgOption.url, imgOption.x, imgOption.y, imgOption.w, imgOption.h || imgOption.w);
	ctx.restore();
};
type textOption = {
	x: number;
	y: number;
	text: string | number;
	font: string;
	size: number;
	fillStyle?: string;
	align?: 'left' | 'center' | 'right';
	alignItemCenter?: boolean; //上下居中，为true则绘制区域从y+size的一半开始
};
// 绘制文本
export const drawText = (ctx: Ctx, { x, y, text, font, size = 14, fillStyle = '#333', align = 'left', alignItemCenter = false }: textOption) => {
	ctx.setTextAlign(align);
	ctx.setFontSize(size);
	font && (ctx.font = font); // 字体样式
	ctx.setFillStyle(fillStyle);
	ctx.fillText(`${text}`, x, y + (alignItemCenter ? size / 2 : 0));
};
const pattern = new RegExp('[A-Za-z]+');
const checkTextIsExceedsWidth = (ctx: Ctx, temp: string, w: number) => {
	let row = [];
	let str = '';
	for (let i = temp.length; i > 0; i--) {
		// 当字符宽度大于等于容器宽度时，如果刚好等于容器宽度，则可以直接使用，否则要递归减去字符末尾，直至字符宽度小于容器宽度
		//字符末尾如果是字母，还应继续递归，直至字符末尾不为字母为止
		let width = ctx.measureText(temp).width;
		let item = temp.substring(i - 1, i);
		if (width > w || pattern.test(item)) {
			str = item + str;
			temp = temp.substring(0, i - 1);
		} else {
			row.push(temp);
			temp = str === ' ' ? '' : str;
			break;
		}
	}

	return {
		row,
		temp
	};
};

// 将超过容器宽度的文本换行
// 返回文本所占高度与文本列表，列表长度即行数
export const getTextLineHeight = (ctx: Ctx, { w, text, lineHeight = 30 }: { w: number; text: string; lineHeight?: number }) => {
	let chr = text.split('');
	let temp = '';
	let row = [];
	for (let a = 0; a < chr.length; a++) {
		//判断是英文，则应凑齐整个单词再判断是否换行，如果整个单词放不下，则要将该单词换到下一行
		if (!pattern.test(chr[a])) {
			if (ctx.measureText(temp).width >= w) {
				let data = checkTextIsExceedsWidth(ctx, temp, w);
				row.push(...data.row);
				temp = data.temp;
			}
		}

		temp += chr[a];
	}
	if (row.length === 0 && ctx.measureText(temp).width > w) {
		//只有一行文本时，再校验一次，因为文本是英文的情况下会存在一行文本减去最后一个单词则宽度小于容器宽度，不减去则超过容器宽度过多的情况
		// 此时应将最后一个单词换行至第二行
		let data = checkTextIsExceedsWidth(ctx, temp, w);
		row = data.row;
		if (data.temp) {
			row.push(data.temp);
		}
	} else {
		row.push(temp);
	}
	let textHeight = row.length * lineHeight;
	return {
		textHeight,
		row
	};
};
type EllipsisTextOption = {
	x: number;
	y: number;
	w: number;
	lineLength: number; //文本应在第几行省略
	lineHeight: number;
	text: string | number;
	font: string;
	size: number;
	fillStyle?: string;
	align?: 'left' | 'center' | 'right';
};
// 绘制文本,当超出指定宽度时，将省略文本
export const drawEllipsisText = (ctx: Ctx, { x, y, w, lineLength = 1, lineHeight, text, font, size = 14, fillStyle = '#333', align = 'left' }: EllipsisTextOption) => {
	ctx.setFontSize(size);
	font && (ctx.font = font); // 字体样式
	let { row } = getTextLineHeight(ctx, {
		w,
		text: `${text}`,
		lineHeight
	});

	let textHeight = 0;
	let hasBreak = false;

	for (let index in row) {
		let length = Number(index) + 1;
		let item = row[index];
		let text = item;

		if (length >= lineLength && length !== row.length) {
			text = item + '...';
			// 在指定的省略行数中时，且不是最后一行，则需绘制省略号
			let width = ctx.measureText(text).width;
			if (width > w) {
				let stringWidth = w - ctx.measureText('...').width;
				const { row: textList } = checkTextIsExceedsWidth(ctx, item, stringWidth);
				text = textList[0] + '...';
			}
			hasBreak = true;
		}
		textHeight += lineHeight;
		drawText(ctx, {
			x: x,
			y: y + length * 44,
			text,
			size,
			font,
			fillStyle,
			align
		});
		if (hasBreak) {
			return textHeight;
		}
	}
	return textHeight;
};
type ArrowRectangleOption = {
	x: number;
	y: number;
	radius: number;
	width: number;
	height: number;
	lineWidth?: number;
	strokeStyle?: string;
	fillStyle?: string;
};
// 绘制 圆角矩形
export const drawArrowRectangle = (ctx: Ctx, { strokeStyle = '#fff', fillStyle = '#fff', x, y, width, height, radius, lineWidth = 2 }: ArrowRectangleOption) => {
	ctx.beginPath();
	ctx.moveTo(x + radius, y);
	ctx.arcTo(x + width, y, x + width, y + height, radius);
	ctx.arcTo(x + width, y + height, x, y + height, radius);
	ctx.arcTo(x, y + height, x, y, radius);
	ctx.lineTo(x, y + 54);
	ctx.lineTo(x - 12, y + 43);
	ctx.lineTo(x, y + 32);
	ctx.lineTo(x, y + radius);
	ctx.arcTo(x, y, x + radius, y, radius);
	ctx.lineWidth = lineWidth;

	ctx.strokeStyle = strokeStyle;
	ctx.fillStyle = fillStyle;
	ctx.stroke();
	ctx.fill();
};
